package com.proxy.autoclick.checker.http.proxy;

import java.util.List;

import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import com.proxy.autoclick.common.util.Constants;
import com.proxy.autoclick.common.util.SpringBeanUtil;
import com.proxy.autoclick.common.util.StringUtil;
import com.proxy.autoclick.common.valueobject.Proxy;
import com.proxy.autoclick.common.valueobject.ValidProxyQueue;
import com.proxy.autoclick.ui.layout.MainFrame;

/**
 * Send a request via proxy.
 *
 */
public abstract class ClientExecuteProxy {
	
	private static final Logger LOGGER = Logger.getLogger(ClientExecuteProxy.class);
	
	private HttpHost proxy;
	private HttpGet httpGet;
	private HttpClient httpclient;
	private Proxy myProxy;
	protected MainFrame mainFrame;
	protected ValidProxyQueue validProxyQueue;
	
	public ClientExecuteProxy(HttpClient httpclient,Proxy myProxy,HttpGet httpGet)
	{
		this.httpclient = httpclient;
		this.myProxy = myProxy;
		this.proxy = new HttpHost(myProxy.getHostname(),myProxy.getPort(),myProxy.getSchemeName());
		this.httpGet = httpGet;
		this.clearCookie();
		this.setHeaders(this.httpGet);
		this.setCookie(this.httpGet);
		this.setParams(httpGet);
		this.mainFrame = (MainFrame) SpringBeanUtil.getSpringBean("mainFrame");
		this.validProxyQueue = (ValidProxyQueue) SpringBeanUtil.getSpringBean("validProxyQueue");
	}

	/**
	 * Check proxy if valid.
	 * @throws Exception
	 */
	public Document execue() throws Exception {
		
		Document doc = null;
		try {
			System.setErr(null);
			Thread.sleep(this.getVisitPageDelay());

			HttpResponse response = httpclient.execute(this.httpGet);
			
			logGetRequest(httpGet);
			logGetResponse(response);
			
			HttpEntity entity = response.getEntity();

			boolean statusResult = response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;
			boolean result = statusResult;
			
			String responseContent = "";
			if (entity != null) {
				responseContent = EntityUtils.toString(entity,Consts.UTF_8);
				doc = Jsoup.parse(responseContent);
			}
			
			if(statusResult)
			{
//				System.out.println(doc);
				boolean checkGooglePattern = StringUtil.checkStringIfFollowPattern(responseContent,this.getRegexString());
				result = checkGooglePattern;
			}
			
			if(statusResult && checkHarmfulContent(responseContent))
			{
				result = false;
			}
			
			if(result)
			{
				this.setValidOutputMessage(this.proxy.toHostString() + this.getValidDisplayString());
				this.updateResultByValid(myProxy);
//				LOGGER.info(doc);
			}
			else
			{
				this.setInValidOutputMessage(this.proxy.toHostString() + this.getInvalidDisplayString());
				this.updateResultByInvalid(myProxy);
				doc = null;
			}
			
			
			
			
		} catch (Exception e) {
//			e.printStackTrace();
			this.setInValidOutputMessage(this.proxy.toHostString() + this.getInvalidDisplayString());
			httpGet.abort();
			this.updateResultByInvalid(myProxy);
		} finally {
			httpGet.releaseConnection();
		}
		return doc;
	}
	
	/**
	 * Check the response content if it is harmful.
	 * @param candidate
	 * @return
	 */
	protected boolean checkHarmfulContent(String candidate)
	{
		String [] regexString = {
				"computer virus or spyware application",
				"entire network is affected",
				"http://www.download.com/Antivirus",
				"/images/yellow_warning.gif",
				"sending automated queries"};
	    
		return StringUtil.checkStringIfFollowPattern(candidate, regexString);
	}
	
	/*private void setValidOutputMessage(String message)
	{
		System.out.println(message);
//		mainFrame.setTextAreaText(message);
	}
	
	private void setInValidOutputMessage(String message)
	{
		System.out.println(message);
	}*/
	
	private void setHeaders(HttpGet httpGet) {
	    httpGet.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;");
	    httpGet.setHeader("Accept-Language", "en-US");
	    httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3");
	    httpGet.setHeader("Accept-Charset", "UTF-8");
	    httpGet.setHeader("Keep-Alive", "300");
	    httpGet.setHeader("Connection", "Keep-Alive");
	    httpGet.setHeader("Cache-Control", "no-cache");
	}
	
	private void setCookie(HttpGet httpGet)
	{
		httpclient.getParams().setParameter(ClientPNames.COOKIE_POLICY,CookiePolicy.BROWSER_COMPATIBILITY);
		httpclient.getParams().setParameter("http.protocol.single-cookie-header", true);
	}
	
	private void clearCookie()
	{
		((DefaultHttpClient)this.httpclient).getCookieStore().clear();
	}
	
	private void setParams(HttpGet httpGet)
	{
		httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
				this.proxy);
		//CONNECTION_TIMEOUT
		httpclient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, Constants.CONNECTION_TIMEOUT); 
		//SO_TIMEOUT
		httpclient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, Constants.READ_TIMEOUT);
		httpclient.getParams().setParameter("http.connection-manager.timeout", Constants.CONNECTION_MANAGER_TIMEOUT);
		
		httpclient.getParams().setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024);
	}
	
	private void logGetRequest(HttpGet httpGet)  {
		LOGGER.info("do get request: " + httpGet.getURI().toString());
		LOGGER.info("header:\n" + getHeadersStr(httpGet.getAllHeaders()));
		LOGGER.info("cookie:\n" + getCookieStr());
	}

	private void logGetResponse(HttpResponse response)  {
	    LOGGER.info("do get response: " + response.getLastHeader("Location"));
	    LOGGER.info("header: \n" + getHeadersStr(response.getAllHeaders()));
	    LOGGER.info("body: \n" + response);
	}

	private void logPostRequest(HttpPost httpPost) {
	    LOGGER.debug("do post request: " + httpPost.getURI().toString());
	    LOGGER.debug("header:\n" + getHeadersStr(httpPost.getAllHeaders()));
	    LOGGER.debug("body:\n" + httpPost.getEntity());
	    LOGGER.debug("cookie:\n" + getCookieStr());
	}

	private void logPostResponse(HttpPost httpPost) {
	    LOGGER.debug("do post response:" + httpPost.getURI().toString());
	    LOGGER.debug("header:\n" + getHeadersStr(httpPost.getAllHeaders()));
	    LOGGER.debug("body:\n" + httpPost.getEntity());
	}

	private String getHeadersStr(Header[] headers) {
	    StringBuilder builder = new StringBuilder();
	    for (Header header : headers) {
	        builder.append(header.getName()).append(": ").append(header.getValue()).append("\n");
	    }
	    return builder.toString();
	}

	/*private String getPostBody(HttpParams httpParams) {
	    StringBuilder builder = new StringBuilder();httpParams.
	    for (NameValuePair pair : httpParams) {
	        builder.append(pair.getName()).append(":").append(pair.getValue()).append("\n");
	    }
	    return builder.toString();
	}*/

	private String getCookieStr() {
	    List<Cookie> cookies = ((DefaultHttpClient)httpclient).getCookieStore().getCookies();
	    StringBuilder builder = new StringBuilder();
	    for (Cookie cookie : cookies) {
	        builder.append(cookie.getDomain()).append(":")
	               .append(cookie.getName()).append("=").append(cookie.getValue()).append(";")
	               .append(cookie.getPath()).append(";")
	               .append(cookie.getExpiryDate()).append(";")
	               .append(cookie.isSecure()).append(";\n");
	    }
	    return builder.toString();
	}
	
	/**
	 * get valid display string which implement in sub class.
	 * @return
	 */
	public abstract String getValidDisplayString();
	
	/**
	 * get invalid display string which implement in sub class.
	 * @return
	 */
	public abstract String getInvalidDisplayString();
	
	/**
	 * update validForGoogleHost or validForOtherHost 
	 * to true for myProxy object, which implement in sub class.
	 * @param myProxy
	 */
	public abstract void updateResultByValid(Proxy myProxy);
	
	/**
	 * update validForGoogleHost or validForOtherHost 
	 * to false for myProxy object, which implement in sub class.
	 * @param myProxy
	 */
	public abstract void updateResultByInvalid(Proxy myProxy);
	
	public abstract String [] getRegexString();
	
	public abstract int getVisitPageDelay();
	
	public abstract void setValidOutputMessage(String message);
	
	public abstract void setInValidOutputMessage(String message);
	
	public  static void main(String args[])
	{
		String candidate = "<!doctype html><html itemscope=\"itemscope\" itemtype=\"http://schema.org/WebPage\"><head><meta itemprop=\"image\" content=\"/images/google_favicon_128.png\"><title>Google</title><script>";
		
		System.out.println(StringUtil.checkStringIfFollowPattern(candidate, Constants.GOOGLE_REGEX_STRING));
	}
	
}
